home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
The World of Computer Software.iso
/
srcuc.zip
/
FFT.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-12-20
|
60KB
|
1,744 lines
/* -*-C-*-
$Header: /scheme/users/cph/src/microcode/RCS/fft.c,v 9.30 1991/12/20 22:49:07 cph Exp $
Copyright (c) 1987-91 Massachusetts Institute of Technology
This material was developed by the Scheme project at the Massachusetts
Institute of Technology, Department of Electrical Engineering and
Computer Science. Permission to copy this software, to redistribute
it, and to use it for any purpose is granted, subject to the following
restrictions and understandings.
1. Any copy made of this software must include this copyright notice
in full.
2. Users of this software agree to make their best efforts (a) to
return to the MIT Scheme project any improvements or extensions that
they make, so that these may be included in future releases; and (b)
to inform MIT of noteworthy uses of this software.
3. All materials developed as a consequence of the use of this
software shall duly acknowledge such use, in accordance with the usual
standards of acknowledging credit in academic research.
4. MIT has made no warrantee or representation that the operation of
this software will be error-free, and MIT is under no obligation to
provide any services, by way of maintenance, update, or otherwise.
5. In conjunction with products arising from the use of this material,
there shall be no use of the name of the Massachusetts Institute of
Technology nor of any adaptation thereof in any advertising,
promotional, or sales literature without prior written consent from
MIT in each case. */
/* Time-Frequency Transforms (pas) */
#include "scheme.h"
#include "prims.h"
#include "zones.h"
#include <math.h>
#include "array.h"
#include "image.h"
/* SUMMARY
- pas_cft (complex data, DIF, split-radix)
- pas_rft (real data, DIT, split-radix) output is conjugate-symmetric
- pas_csft (cs data, DIF, split-radix) output is real
- pas_cft
- pas_rft2d
- pas_csft2d
Stuff before 4-15-1989
- C_Array_FFT (complex data, radix=2, NOT-in-place)
- CZT (chirp-z-transform) uses the old cft (hence slow).
- 2d DFT
*/
/* The DFT is as defined in Siebert 6003 book,
i.e.
forward DFT = Negative exponent and division by N
backward DFT = Positive exponent
(note Seibert forward DFT is Oppenheim backward DFT)
*/
/* mathematical constants */
#ifdef PI
#undef PI
#endif
#define PI 3.141592653589793238462643
#define TWOPI 6.283185307179586476925287
#define SQRT_2 1.4142135623730950488
#define ONE_OVER_SQRT_2 .7071067811865475244
/* Abramowitz and Stegun */
DEFINE_PRIMITIVE ("PAS-CFT!", Prim_pas_cft, 5, 5, 0)
{ long i, length, power, flag;
REAL *f1,*f2, *wcos,*w3cos,*w3sin;
void pas_cft();
PRIMITIVE_HEADER (5);
CHECK_ARG (2, ARRAY_P); /* real part */
CHECK_ARG (3, ARRAY_P); /* imag part */
CHECK_ARG (4, ARRAY_P); /* twiddle tables, total length = 3*(length/4) */
CHECK_ARG (5, FIXNUM_P); /* (1)=tables precomputed, else recompute */
flag = (arg_integer (1));
length = ARRAY_LENGTH(ARG_REF(2));
if (length != (ARRAY_LENGTH(ARG_REF(3)))) error_bad_range_arg(2);
for (power=0, i=length; i>1; power++)
{ if ( (i % 2) == 1) error_bad_range_arg(2);
i=i/2; }
f1 = ARRAY_CONTENTS(ARG_REF(2));
f2 = ARRAY_CONTENTS(ARG_REF(3));
if (f1==f2) error_wrong_type_arg(2);
wcos = ARRAY_CONTENTS(ARG_REF(4)); /* twiddle tables */
if (ARRAY_LENGTH(ARG_REF(4)) != (3*length/4)) error_bad_range_arg(4);
w3cos = wcos + length/4;
w3sin = w3cos + length/4;
if ((arg_nonnegative_integer(5)) == 1)
pas_cft(1, flag, f1,f2, length, power, wcos,w3cos,w3sin);
else
pas_cft(0, flag, f1,f2, length, power, wcos,w3cos,w3sin);
/* 1 means tables are already made
0 means compute new tables */
PRIMITIVE_RETURN (UNSPECIFIC);
}
DEFINE_PRIMITIVE ("PAS-CFT-MAKE-TWIDDLE-TABLES!",
Prim_pas_cft_make_twiddle_tables, 2, 2, 0)
{ long length, power, i;
REAL *wcos,*w3cos,*w3sin;
void pas_cft_make_twiddle_tables_once();
PRIMITIVE_HEADER (2);
length = arg_nonnegative_integer(1); /* length of cft that we intend to compute */
CHECK_ARG (2, ARRAY_P); /* storage for twiddle tables */
if (ARRAY_LENGTH(ARG_REF(2)) != (3*length/4)) error_bad_range_arg(2);
power=0;
for (power=0, i=length; i>1; power++)
{ if ( (i % 2) == 1) error_bad_range_arg(1);
i=i/2; }
wcos = ARRAY_CONTENTS(ARG_REF(2)); /* twiddle tables */
w3cos = wcos + length/4;
w3sin = w3cos + length/4;
pas_cft_make_twiddle_tables_once(length, power, wcos,w3cos,w3sin);
PRIMITIVE_RETURN (UNSPECIFIC);
}
/*
C COMPLEX FOURIER TRANSFORM (Split-Radix, Decimation-in-frequency)
C (adapted and optimized from Sorensen,et.al. ASSP-34 no.1 page 152, February 1986)
*/
/* Twiddle Tables for PAS_CFT;
(tables for forward transform only)
Inverse transform === forward CFT (without 1/N scaling) followed by time-reversal.
/
The tables contain (2pi/N)*i for i=0,1,2,..,N/4
(except i=0 is ignored, never used)
/
Table for wsin[i] is not needed because wsin[i]=wcos[n4-i].
Table for w3sin[i] is needed however. The previous relationship does not work for w3sin.
*/
/* There are two routines for making twiddle tables:
a fast one, and a slower one but more precise.
The differences in speed and accuracy are actually rather small, but anyway.
Use the slow one for making permanent tables.
*/
void
pas_cft_make_twiddle_tables (n,m, wcos,w3cos,w3sin) /* efficient version */
REAL *wcos, *w3cos, *w3sin;
long n,m;
{ long i, n4;
double tm;
REAL costm,sintm;
n4 = n/4;
for (i=1; i<n4; i++) /* start from table entry 1 */
{ tm = 6.283185307179586476925287 * (((double) i) / ((double) n));
wcos[i] = (REAL) cos(tm);
}
for (i=1; i<n4; i++)
{ costm = wcos[i];
sintm = wcos[n4-i];
w3cos[i] = costm * (1 - 4*sintm*sintm); /* see my notes */
w3sin[i] = sintm * (4*costm*costm - 1);
}
}
void
pas_cft_make_twiddle_tables_once (n,m, wcos,w3cos,w3sin) /* slow version, more accurate */
REAL *wcos, *w3cos, *w3sin;
long n,m;
{ long i, n4;
double tm;
REAL costm,sintm;
n4 = n/4;
for (i=1; i<n4; i++) /* start from table entry 1 */
{ tm = 6.283185307179586476925287 * (((double) i) / ((double) n));
wcos[i] = (REAL) cos(tm);
tm = tm * 3.0; /* this is more precise (in the 16th decimal) than */
w3cos[i] = (REAL) cos(tm); /* the more efficient version. (I tested by for/backward) */
w3sin[i] = (REAL) sin(tm);
}
}
void
pas_cft (tables_ok,flag, x,y,n,m, wcos,w3cos,w3sin)
REAL *x,*y, *wcos,*w3cos,*w3sin;
long n,m, flag, tables_ok;
{ REAL scale;
long i,j;
void pas_cft_make_twiddle_tables();
void C_Array_Time_Reverse();
void pas_cft_forward_loop();
if (tables_ok != 1) /* 1 means = tables already made */
pas_cft_make_twiddle_tables(n,m, wcos,w3cos,w3sin);
if (flag == 1) /* forward cft */
{ pas_cft_forward_loop(x,y,n,m, wcos,w3cos,w3sin);
scale = (REAL) (1.0 / ((double) n));
for (i=0; i<n; i++)
{ x[i] = x[i]*scale;
y[i] = y[i]*scale; }}
else /* backward cft */
{ for (j=0; j<n; j++) y[j] = (-y[j]); /* conjugate before */
pas_cft_forward_loop(x,y,n,m, wcos,w3cos,w3sin);
for (j=0; j<n; j++) y[j] = (-y[j]); /* conjugate after */
}
}
void
pas_cft_forward_loop (x,y,n,m, wcos,w3cos,w3sin) /* n >= 4 */
REAL *x,*y, *wcos,*w3cos,*w3sin;
long n,m;
{ /* REAL a,a3,e; no need anymore, use tables */
REAL r1,r2,s1,s2,s3, xt, cc1,cc3,ss1,ss3;
long n1,n2,n4, i,j,k, is,id, i0,i1,i2,i3;
long windex0, windex, windex_n4; /* indices for twiddle tables */
/********** fortran indices start from 1,... **/
x = x-1; /* TRICK---- x(0) is now illegal, but x(1) and x(n) are valid */
y = y-1;
/********** fortran indices start from 1,... **/
/* c */
/* c-----first M-1 stages of transform */
/* c */
windex_n4 = n/4; /* need for indexing sin via wcos twiddle table */
n2 = 2*n;
for (k=1; k<m; k++) /* DO 10 K = 1, M-1 */
{ n2 = n2>>1; /* n2 = n2/2; */
n4 = n2>>2; /* n4 = n2/4; */
/* e = 6.283185307179586476925287 / ((REAL) n2); no need anymore, use tables */
/* a = 0.0; */
{
/* j=1; */
/* The first iteration in the loop "DO 20 J = 1, N4"
is done specially to save operations involving sin=0, cos=1 */
/* a = j*e; no need anymore, use tables */
is = 1; /* is = j; */
id = 2*n2;
label40first:
for (i0=is; i0<n; i0=i0+id) /* 40 DO 30 I0 = IS,N-1,ID */
{ i1 = i0 + n4;
i2 = i1 + n4;
i3 = i2 + n4;
/* c */
r1 = x[i0] - x[i2];
x[i0] = x[i0] + x[i2];
r2 = x[i1] - x[i3];
x[i1] = x[i1] + x[i3];
s1 = y[i0] - y[i2];
y[i0] = y[i0] + y[i2];
s2 = y[i1] - y[i3];
y[i1] = y[i1] + y[i3];
/* c */
s3 = r1 - s2;
r1 = r1 + s2;
s2 = s1 - r2; /* original used to be s2 = r2 - s1; */
r2 = r2 + s1;
x[i2] = r1;
y[i2] = s2; /* used to be y[i2] = (-s2); */
x[i3] = s3;
y[i3] = r2;
/* x[i2] = r1*cc1 + s2*ss1; used to be, see below
y[i2] = s2*cc1 - r1*ss1; used to be, see below, inside the DO 20 J=1,N4
x[i3] = s3*cc3 + r2*ss3;
y[i3] = r2*cc3 - s3*ss3; */
} /* 30 CONTINUE */
is = 2*id - n2 + 1; /* is = 2*id - n2 + j; */
id = 4*id;
if (is < n) goto label40first; /* IF (IS.LT.N) GOTO 40 */
}
/* c */
windex0 = 1<<(k-1);
windex = windex0;
for (j=2; j<=n4; j++) /* DO 20 J = 1, N4 */
{
/* windex = (j-1)*(1<<(k-1)); -- done with trick to avoid (j-1) and 1<<(k-1) */
cc1 = wcos[windex];
ss1 = wcos[windex_n4 - windex]; /* see my notes */
cc3 = w3cos[windex];
ss3 = w3sin[windex]; /* sin-from-cos trick does not work here */
windex = j*windex0; /* same trick as "a = j*e" */
/* a3 = 3*a;
cc1 = cos(a);
ss1 = sin(a);
cc3 = cos(a3);
ss3 = sin(a3);
a = j*e;*/
is = j;
id = 2*n2;
label40:
for (i0=is; i0<n; i0=i0+id) /* 40 DO 30 I0 = IS,N-1,ID */
{ i1 = i0 + n4;
i2 = i1 + n4;
i3 = i2 + n4;
/* c */
r1 = x[i0] - x[i2];
x[i0] = x[i0] + x[i2];
r2 = x[i1] - x[i3];
x[i1] = x[i1] + x[i3];
s1 = y[i0] - y[i2];
y[i0] = y[i0] + y[i2];
s2 = y[i1] - y[i3];
y[i1] = y[i1] + y[i3];
/* c */
s3 = r1 - s2;
r1 = r1 + s2;
s2 = s1 - r2; /* original used to be s2 = r2 - s1; */
r2 = r2 + s1;
x[i2] = r1*cc1 + s2*ss1; /* used to be x[i2] = r1*cc1 - s2*ss1; */
y[i2] = s2*cc1 - r1*ss1; /* used to be y[i2] = (-s2*cc1 - r1*ss1); */
x[i3] = s3*cc3 + r2*ss3;
y[i3] = r2*cc3 - s3*ss3;
} /* 30 CONTINUE */
is = 2*id - n2 + j;
id = 4*id;
if (is < n) goto label40; /* IF (IS.LT.N) GOTO 40 */
} /* 20 CONTINUE */
} /* 10 CONTINUE */
/* c
c-----------last-stage, length-2 butterfly ----------------c
c */
is = 1;
id = 4;
label50:
for (i0=is; i0<=n; i0=i0+id) /* 50 DO 60 I0 = IS, N, ID */
{ i1 = i0 + 1;
r1 = x[i0];
x[i0] = r1 + x[i1];
x[i1] = r1 - x[i1];
r1 = y[i0];
y[i0] = r1 + y[i1];
y[i1] = r1 - y[i1];
} /* 60 CONTINUE */
is = 2*id - 1;
id = 4*id;
if (is < n) goto label50; /* IF (IS.LT.N) GOTO 50 */
/*
c
c-----------bit-reverse-counter---------------c
*/
label100:
j = 1;
n1 = n - 1;
for (i=1; i<=n1; i++) /* DO 104 I = 1, N1 */
{ if (i >= j) goto label101; /* if (i .ge. j) goto 101 */
xt = x[j];
x[j] = x[i];
x[i] = xt;
xt = y[j];
y[j] = y[i];
y[i] = xt;
label101: k = n>>1; /* k = n/2; */
label102: if (k>=j) goto label103;
j = j - k;
k = k>>1; /* k = k/2; */
goto label102;
label103: j = j + k;
} /* 104 CONTINUE */
/* c-------------------------------------*/
/* c */
} /* RETURN END */
DEFINE_PRIMITIVE ("PAS-RFT-CSFT!", Prim_pas_rft_csft, 5, 5, 0)
{ long i, length, power, flag, ft_type;
REAL *f1, *wcos,*w3cos,*w3sin;
void pas_rft(), pas_csft();
PRIMITIVE_HEADER (5);
CHECK_ARG (2, ARRAY_P); /* Input data (real or cs) */
CHECK_ARG (3, ARRAY_P); /* Twiddle tables, total length = 4*(length/8) */
CHECK_ARG (4, FIXNUM_P); /* (1)=tables precomputed, else recompute */
CHECK_ARG (5, FIXNUM_P); /* ft_type = 1 or 3
1 means compute rft, 3 means compute csft */
flag = (arg_integer (1));
f1 = ARRAY_CONTENTS(ARG_REF(2));
length = ARRAY_LENGTH(ARG_REF(2));
for (power=0, i=length; i>1; power++)
{ if ( (i % 2) == 1) error_bad_range_arg(2);
i=i/2; }
wcos = ARRAY_CONTENTS(ARG_REF(3)); /* twiddle tables */
if (ARRAY_LENGTH(ARG_REF(3)) != (4*length/8)) error_bad_range_arg(3);
w3cos = wcos + (length/4);
w3sin = w3cos + (length/8);
ft_type = (arg_nonnegative_integer(5)); /* rft or csft */
if (ft_type == 1) {
if ((arg_nonnegative_integer(4)) == 1)
pas_rft (1, flag, f1, length, power, wcos,w3cos,w3sin);
else pas_rft (0, flag, f1, length, power, wcos,w3cos,w3sin);
}
else if (ft_type == 3) {
if ((arg_nonnegative_integer(4)) == 1)
pas_csft (1, flag, f1, length, power, wcos,w3cos,w3sin);
else pas_csft (0, flag, f1, length, power, wcos,w3cos,w3sin);
/* 1 means tables are already made
0 means compute new tables */
}
else error_bad_range_arg(5);
PRIMITIVE_RETURN (UNSPECIFIC);
}
DEFINE_PRIMITIVE ("PAS-REALDATA-MAKE-TWIDDLE-TABLES!",
Prim_pas_realdata_make_twiddle_tables, 2, 2, 0)
{ long length, power, i;
REAL *wcos,*w3cos,*w3sin;
void pas_realdata_make_twiddle_tables_once();
PRIMITIVE_HEADER (2);
length = arg_nonnegative_integer(1); /* length of rft that we intend to compute */
CHECK_ARG (2, ARRAY_P); /* storage for twiddle tables */
if (ARRAY_LENGTH(ARG_REF(2)) != (4*length/8)) error_bad_range_arg(2);
power=0;
for (power=0, i=length; i>1; power++)
{ if ( (i % 2) == 1) error_bad_range_arg(1);
i=i/2; }
wcos = ARRAY_CONTENTS(ARG_REF(2)); /* twiddle tables */
w3cos = wcos + length/4;
w3sin = w3cos + length/8;
pas_realdata_make_twiddle_tables_once(length, power, wcos,w3cos,w3sin);
PRIMITIVE_RETURN (UNSPECIFIC);
}
/*
C REAL FOURIER TRANSFORM (Split-Radix, Decimation-in-time)
C (adapted from Sorensen,et.al. ASSP-35 no.6 page 849, October 1986)
C
C the output is [Re(0),Re(1),...,Re(n/2), Im(n/2-1),...,Im(1)]
*/
/* Twiddle Tables for PAS_RFT and PAS_CSFT
are identical. -> pas_realdata_make_twiddle_tables
(but they are indexed differently in each case)
/
The tables contain (2pi/N)*i where i=0,1,2,..,N/4 for wcos
and (2pi/N)*i where i=0,1,2,..,N/8 for w3cos w3sin
(except i=0 is ignored, never used)
/
Table for wsin[i] is not needed because wsin[i]=wcos[n4-i].
Table for w3sin[i] is needed however. The previous relationship does not work for w3sin.
/
Instead of getting sin() from a wsin[i] table with i=1,..,N/8
we get it from wcos[n4-i].
This way we can use a CFT table which goes up to N/4
for RFT CSFT also. We do so in image-processing (rft2d-csft2d).
*/
/* There are two routines for making twiddle tables:
a fast one, and a slower one but more precise.
The differences in speed and accuracy are actually rather small, but anyway.
Use the slow one for making tables that stay around.
*/
void pas_realdata_make_twiddle_tables(n,m, wcos,w3cos,w3sin) /* efficient version */
REAL *wcos, *w3cos, *w3sin;
long n,m;
{ long i, n4, n8;
double tm;
REAL costm,sintm;
n4 = n/4;
n8 = n/8;
for (i=1; i<n4; i++) /* start from table entry 1 */
{ tm = 6.283185307179586476925287 * (((double) i) / ((double) n));
wcos[i] = (REAL) cos(tm);
}
for (i=1; i<n8; i++)
{ costm = wcos[i];
sintm = wcos[n4-i];
w3cos[i] = costm * (1 - 4*sintm*sintm); /* see my notes */
w3sin[i] = sintm * (4*costm*costm - 1);
}
}
void pas_realdata_make_twiddle_tables_once(n,m, wcos,w3cos,w3sin) /* slow version, more accurate */
REAL *wcos, *w3cos, *w3sin;
long n,m;
{ long i, n4, n8;
double tm;
REAL costm,sintm;
n4 = n/4;
n8 = n/8;
for (i=1; i<n8; i++) /* start from table entry 1 */
{ tm = 6.283185307179586476925287 * (((double) i) / ((double) n));
wcos[i] = (REAL) cos(tm);
tm = tm * 3.0; /* this is more precise (in the 16th decimal) than */
w3cos[i] = (REAL) cos(tm); /* the more efficient version. (I tested by for/backward) */
w3sin[i] = (REAL) sin(tm);
}
for (i=n8; i<n4; i++)
{ tm = 6.283185307179586476925287 * (((double) i) / ((double) n));
wcos[i] = (REAL) cos(tm);
}
}
void pas_rft(tables_ok,flag, x,n,m, wcos,w3cos,w3sin)
REAL *x, *wcos,*w3cos,*w3sin;
long n,m, flag, tables_ok;
{ REAL scale;
long i;
void pas_realdata_make_twiddle_tables();
void pas_rft_forward_loop();
if (tables_ok != 1) /* 1 means = tables already made */
pas_realdata_make_twiddle_tables(n,m, wcos,w3cos,w3sin);
pas_rft_forward_loop(x,n,m, wcos,w3cos,w3sin);
if (flag == 1) /* forward rft */
{ scale = (REAL) (1.0 / ((double) n));
for (i=0; i<n; i++) x[i] = x[i] * scale; }
else /* backward rft */
for (i=((n/2)+1); i<n; i++) x[i] = (-x[i]); /* time-reverse cs-array */
}
/* rft
forward transform === forward_loop + 1/N scaling
inverse transform === forward_loop + time-reversal (without 1/N scaling)
*/
/* wcos must be length n/4
w3cos, w3sin must be length n/8
(greater than n/8 is fine also, e.g. use cft tables)
*/
void pas_rft_forward_loop(x,n,m, wcos,w3cos,w3sin)
REAL *x, *wcos,*w3cos,*w3sin;
long n,m;
{ /* REAL a,a3,e; no need anymore, use tables */
REAL r1, xt, cc1,cc3,ss1,ss3, t1,t2,t3,t4,t5,t6;
long n1,n2,n4,n8, i,j,k, is,id, i0,i1,i2,i3,i4,i5,i6,i7,i8;
long windex0, windex, windex_n4; /* indices for twiddle tables */
/********** fortran indices start from 1,... **/
x = x-1; /* TRICK---- x(0) is now illegal, but x(1) and x(n) are valid */
/********** fortran indices start from 1,... **/
/* c */
windex_n4 = n/4; /* need for indexing sin via wcos twiddle table */
/* c
c-----------bit-reverse-counter---------------c
*/
label100:
j = 1;
n1 = n - 1;
for (i=1; i<=n1; i++) /* DO 104 I = 1, N1 */
{ if (i >= j) goto label101; /* if (i .ge. j) goto 101 */
xt = x[j];
x[j] = x[i];
x[i] = xt;
label101: k = n>>1; /* k = n/2; */
label102: if (k>=j) goto label103;
j = j - k;
k = k>>1; /* k = k/2; */
goto label102;
label103: j = j + k;
} /* 104 CONTINUE */
/* c-------------------------------------*/
/* c */
/* c ----length-two-butterflies----------- */
is = 1;
id = 4;
label70:
for (i0=is; i0<=n; i0=i0+id) /* 70 DO 60 I0 = IS,N,ID */
{ i1 = i0 + 1;
r1 = x[i0];
x[i0] = r1 + x[i1];
x[i1] = r1 - x[i1];
} /* 60 CONTINUE */
is = 2*id - 1;
id = 4*id;
if (is < n) goto label70; /* IF (IS.LT.N) GOTO 70 */
/* C
C -------L-shaped-butterflies-------- */
n2 = 2;
for (k=2; k<=m; k++) /* DO 10 K = 2,M */
{ n2 = n2 * 2;
n4 = n2>>2; /* n4 = n2/4; */
n8 = n2>>3; /* n8 = n2/8; */
/* e = 6.283185307179586476925287 / ((REAL) n2); no need anymore, use tables */
is = 0;
id = n2 * 2;
label40:
for (i=is; i<n; i=i+id) /* 40 DO 38 I = IS,N-1,ID */
{ i1 = i + 1;
i2 = i1 + n4;
i3 = i2 + n4;
i4 = i3 + n4;
t1 = x[i4] + x[i3];
x[i4] = x[i4] - x[i3];
x[i3] = x[i1] - t1;
x[i1] = x[i1] + t1;
if (n4 == 1) goto label38; /* IF (N4.EQ.1) GOTO 38 */
i1 = i1 + n8;
i2 = i2 + n8;
i3 = i3 + n8;
i4 = i4 + n8;
/* t1 = (x[i3] + x[i4]) / sqrt(2.0); -- this is more precise, it uses extended
t2 = (x[i3] - x[i4]) / sqrt(2.0); -- precision inside 68881, but slower */
t1 = (x[i3] + x[i4]) * ONE_OVER_SQRT_2;
t2 = (x[i3] - x[i4]) * ONE_OVER_SQRT_2;
x[i4] = x[i2] - t1;
x[i3] = -x[i2] - t1;
x[i2] = x[i1] - t2;
x[i1] = x[i1] + t2;
label38: /* 38 CONTINUE */
;
}
is = 2*id - n2;
id = 4*id;
if (is < n) goto label40; /* IF (IS.LT.N) GOTO 40 */
/* a = e; */
windex0 = 1<<(m-k);
windex = windex0;
for (j=2; j<=n8; j++) /* DO 32 J = 2,N8 */
{
/* windex = (j-1)*(1<<(m-k)); -- done with trick to avoid (j-1) and 1<<(m-k) */
cc1 = wcos[windex];
ss1 = wcos[windex_n4 - windex]; /* sin-from-cos trick: see my notes */
cc3 = w3cos[windex];
ss3 = w3sin[windex]; /* sin-from-cos trick does not work here */
windex = j*windex0; /* same trick as "a = j*e" */
/* a3 = 3*a;
cc1 = cos(a);
ss1 = sin(a);
cc3 = cos(a3);
ss3 = sin(a3);
a = j*e;*/
is = 0;
id = 2*n2;
label36: /* 36 DO 30 I = IS,N-1,ID */
for (i=is; i<n; i=i+id)
{ i1 = i + j;
i2 = i1 + n4;
i3 = i2 + n4;
i4 = i3 + n4;
i5 = i + n4 - j + 2;
i6 = i5 + n4;
i7 = i6 + n4;
i8 = i7 + n4;
t1 = x[i3]*cc1 + x[i7]*ss1;
t2 = x[i7]*cc1 - x[i3]*ss1;
t3 = x[i4]*cc3 + x[i8]*ss3;
t4 = x[i8]*cc3 - x[i4]*ss3;
t5 = t1 + t3;
t6 = t2 + t4;
t3 = t3 - t1; /* t3 = t1 - t3; */
t4 = t2 - t4;
x[i8] = x[i6] + t6;
x[i3] = t6 - x[i6];
x[i4] = x[i2] + t3; /* x[i4] = x[i2] - t3; */
x[i7] = t3 - x[i2]; /* x[i7] = -x[i2] - t3; */
x[i6] = x[i1] - t5;
x[i1] = x[i1] + t5;
x[i2] = x[i5] + t4;
x[i5] = x[i5] - t4;
} /* 30 CONTINUE */
is = 2*id - n2;
id = 4*id;
if (is < n) goto label36; /* IF (IS.LT.N) GOTO 36 */
} /* 32 CONTINUE */
} /* 10 CONTINUE */
} /* RETURN END */
/*
C CONJUGATE SYMMETRIC FOURIER TRANSFORM (Split-Radix, Decimation-in-time)
C (adapted from Sorensen,et.al. ASSP-35 no.6 page 849, October 1986)
C
C input is [Re(0),Re(1),...,Re(n/2), Im(n/2-1),...,Im(1)]
C output is real
*/
/* twiddle tables identical with rft
for comments see rft */
void pas_csft(tables_ok,flag, x,n,m, wcos,w3cos,w3sin)
REAL *x, *wcos,*w3cos,*w3sin;
long n,m, flag, tables_ok;
{ REAL scale;
long i,n2;
void pas_realdata_make_twiddle_tables();
void C_Array_Time_Reverse();
void pas_csft_backward_loop();
if (tables_ok != 1) /* 1 means = tables already made */
pas_realdata_make_twiddle_tables(n,m, wcos,w3cos,w3sin);
if (flag == 1) /* forward csft */
{ n2 = n/2;
scale = (REAL) (1.0 / ((double) n));
for (i=0; i<=n2; i++) x[i] = x[i]*scale;
scale = (-scale);
for (i=n2+1; i<n; i++) x[i] = x[i]*scale; /* scale and conjugate cs-array */
pas_csft_backward_loop(x,n,m, wcos,w3cos,w3sin);
}
else /* backward csft */
pas_csft_backward_loop(x,n,m, wcos,w3cos,w3sin);
}
/* csft
forward transform === backward_loop + 1/N scaling + time-reversal
inverse transform === backward_loop
*/
/* wcos must be length n/4
w3cos, w3sin must be length n/8
(greater than n/8 is fine also, e.g. use cft tables)
*/
void pas_csft_backward_loop(x,n,m, wcos,w3cos,w3sin)
REAL *x, *wcos,*w3cos,*w3sin;
long n,m;
{ /* REAL a,a3,e; no need anymore, use tables */
REAL r1, xt, cc1,cc3,ss1,ss3, t1,t2,t3,t4,t5;
long n1,n2,n4,n8, i,j,k, is,id, i0,i1,i2,i3,i4,i5,i6,i7,i8;
long windex0, windex, windex_n4; /* indices for twiddle tables */
/********** fortran indices start from 1,... **/
x = x-1; /* TRICK---- x(0) is now illegal, but x(1) and x(n) are valid */
/********** fortran indices start from 1,... **/
/* c */
windex_n4 = n/4; /* need for indexing sin via wcos twiddle table */
/* c */
/* c */
/* c
c -------L-shaped-butterflies-------- */
n2 = 2*n;
for (k=1; k<m; k++) /* do 10 k = 1,m-1 */
{ is = 0;
id = n2;
n2 = n2>>1; /* n2 = n2/2; */
n4 = n2>>2; /* n4 = n2/4; */
n8 = n4>>1; /* n8 = n4/2; */
/* e = 6.283185307179586476925287 / ((REAL) n2); no need anymore, use tables */
label17:
for (i=is; i<n; i=i+id) /* 17 do 15 i = is,(n-1),id */
{ i1 = i + 1;
i2 = i1 + n4;
i3 = i2 + n4;
i4 = i3 + n4;
t1 = x[i1] - x[i3];
x[i1] = x[i1] + x[i3];
x[i2] = 2*x[i2];
x[i3] = t1 - 2*x[i4];
x[i4] = t1 + 2*x[i4];
if (n4 == 1) goto label15; /* if (n4.eq.1) goto 15 */
i1 = i1 + n8;
i2 = i2 + n8;
i3 = i3 + n8;
i4 = i4 + n8;
t1 = (x[i2] - x[i1]) * ONE_OVER_SQRT_2;
t2 = (-(x[i4] + x[i3])) * ONE_OVER_SQRT_2;
/* t1 = (x[i2] - x[i1])/sqrt(2.0);
t2 = (x[i4] + x[i3])/sqrt(2.0); */
x[i1] = x[i1] + x[i2];
x[i2] = x[i4] - x[i3];
x[i3] = 2 * (t2-t1); /* x[i3] = 2 * (-t2-t1); */
x[i4] = 2 * (t2+t1); /* x[i4] = 2 * (-t2+t1); */
label15:
;
} /* 15 continue */
is = 2*id - n2;
id = 4*id;
if (is < (n-1)) goto label17; /* if (is.lt.(n-1)) goto 17 */
/* a = e; */
windex0 = 1<<(k-1); /* see my notes */
windex = windex0;
for (j=2; j<=n8; j++) /* do 20 j=2,n8 */
{
/* windex = (j-1)*(1<<(k-1)); -- done with trick to avoid (j-1) and 1<<(k-1) */
cc1 = wcos[windex];
ss1 = wcos[windex_n4 - windex]; /* sin-from-cos trick: see my notes */
cc3 = w3cos[windex];
ss3 = w3sin[windex]; /* sin-from-cos trick does not work here */
windex = j*windex0; /* same trick as "a = j*e" */
/* a3 = 3*a;
cc1 = cos(a);
ss1 = sin(a);
cc3 = cos(a3);
ss3 = sin(a3);
a = j*e; */
is = 0;
id = 2*n2;
label40:
for (i=is; i<n; i=i+id) /* 40 do 30 i = is,(n-1),id */
{ i1 = i + j;
i2 = i1 + n4;
i3 = i2 + n4;
i4 = i3 + n4;
i5 = i + n4 - j + 2;
i6 = i5 + n4;
i7 = i6 + n4;
i8 = i7 + n4;
t1 = x[i1] - x[i6];
x[i1] = x[i1] + x[i6];
t2 = x[i5] - x[i2];
x[i5] = x[i5] + x[i2];
t3 = x[i8] + x[i3];
x[i6] = x[i8] - x[i3];
t4 = x[i4] + x[i7];
x[i2] = x[i4] - x[i7];
t5 = t1 - t4;
t1 = t1 + t4;
t4 = t2 - t3;
t2 = t2 + t3;
x[i3] = t5*cc1 + t4*ss1;
x[i7] = t5*ss1 - t4*cc1; /* x[i7] = (-t4*cc1) + t5*ss1; */
x[i4] = t1*cc3 - t2*ss3;
x[i8] = t2*cc3 + t1*ss3;
} /* 30 continue */
is = 2*id - n2;
id = 4*id;
if (is < (n-1)) goto label40; /* if (is.lt.(n-1)) goto 40 */
} /* 20 continue */
} /* 10 continue */
/* c */
/* c ----length-two-butterflies----------- */
is = 1;
id = 4;
label70:
for (i0=is; i0<=n; i0=i0+id) /* 70 DO 60 I0 = IS,N,ID */
{ i1 = i0 + 1;
r1 = x[i0];
x[i0] = r1 + x[i1];
x[i1] = r1 - x[i1];
} /* 60 CONTINUE */
is = 2*id - 1;
id = 4*id;
if (is < n) goto label70; /* IF (IS.LT.N) GOTO 70 */
/* c */
/* c-----------bit-reverse-counter---------------c */
label100:
j = 1;
n1 = n - 1;
for (i=1; i<=n1; i++) /* DO 104 I = 1, N1 */
{ if (i >= j) goto label101; /* if (i .ge. j) goto 101 */
xt = x[j];
x[j] = x[i];
x[i] = xt;
label101: k = n>>1; /* k = n/2; */
label102: if (k>=j) goto label103;
j = j - k;
k = k>>1; /* k = k/2; */
goto label102;
label103: j = j + k;
} /* 104 CONTINUE */
/* c */
} /* RETURN END */
/* Image processing only for square images
(old stuff handles non-square but is slow)
For 2d FTs precomputed tables or not, make almost no difference in total time.
*/
DEFINE_PRIMITIVE ("PAS-CFT2D!", Prim_pas_cft2d, 5,5, 0)
{ long i, length, power, flag, rows,rowpower;
REAL *f1,*f2, *wcos,*w3cos,*w3sin;
void pas_cft2d();
PRIMITIVE_HEADER (5);
CHECK_ARG (2, ARRAY_P); /* real part */
CHECK_ARG (3, ARRAY_P); /* imag part */
CHECK_ARG (4, ARRAY_P); /* twiddle tables, length = 3*(rows/4) */
flag = (arg_integer (1));
length = ARRAY_LENGTH(ARG_REF(2));
if (length != (ARRAY_LENGTH(ARG_REF(3)))) error_bad_range_arg(2);
for (power=0, i=length; i>1; power++) /* length must be power of 2 */
{ if ( (i % 2) == 1) error_bad_range_arg(2);
i=i/2; }
if ((power % 2) == 1) error_bad_range_arg(2);
rowpower = (power/2);
rows = (1<<rowpower); /* square image */
f1 = ARRAY_CONTENTS(ARG_REF(2));
f2 = ARRAY_CONTENTS(ARG_REF(3));
if (f1==f2) error_wrong_type_arg(2);
wcos = ARRAY_CONTENTS(ARG_REF(4)); /* twiddle tables */
if (ARRAY_LENGTH(ARG_REF(4)) != (3*rows/4)) error_bad_range_arg(4);
w3cos = wcos + rows/4;
w3sin = w3cos + rows/4;
if ((arg_nonnegative_integer(5)) == 1)
pas_cft2d(1, flag, f1,f2, rows, rowpower, wcos,w3cos,w3sin);
else
pas_cft2d(0, flag, f1,f2, rows, rowpower, wcos,w3cos,w3sin);
/* 1 means tables are already made
0 means compute new tables */
PRIMITIVE_RETURN (UNSPECIFIC);
}
/* pas_cft2d
n = rows of square image, rows is power of 2
m = rowpower
Scaling (1/n) is done all-at-once at the end.
Time-Reversing is done intermediately, it is more efficient.
*/
void pas_cft2d(tables_ok,flag, x,y,n,m, wcos,w3cos,w3sin)
REAL *x,*y, *wcos,*w3cos,*w3sin;
long n,m, flag, tables_ok;
{ REAL scale, *xrow,*yrow;
long i,j, rows,cols, total_length;
void pas_cft_make_twiddle_tables_once();
void C_Array_Time_Reverse();
void pas_cft_forward_loop();
if (tables_ok != 1) /* 1 means = tables already made */
pas_cft_make_twiddle_tables_once(n,m, wcos,w3cos,w3sin);
rows = n;
cols = rows; /* square image */
total_length = rows*rows;
if (flag != 1) /* backward transform */
for (i=0; i<total_length; i++) y[i] = (-y[i]); /* conjugate before */
xrow = x; yrow = y; /* ROW-WISE */
for (i=0; i<rows; i++) /* forward or backward */
{ pas_cft_forward_loop( xrow,yrow, n,m, wcos,w3cos,w3sin);
xrow = xrow + cols;
yrow = yrow + cols; }
Image_Fast_Transpose(x, rows); /* COLUMN-WISE */
Image_Fast_Transpose(y, rows);
xrow = x; yrow = y;
for (i=0; i<rows; i++) /* forward or backward */
{ pas_cft_forward_loop( xrow,yrow, n,m, wcos,w3cos,w3sin);
xrow = xrow + cols;
yrow = yrow + cols; }
Image_Fast_Transpose(x, rows);
Image_Fast_Transpose(y, rows);
if (flag == 1) /* forward : scale */
{ scale = (REAL) (1.0 / ((double) total_length));
for (i=0; i<total_length; i++)
{ x[i] = x[i]*scale;
y[i] = y[i]*scale; }}
else /* backward : conjugate after */
for (i=0; i<total_length; i++) y[i] = (-y[i]);
}
DEFINE_PRIMITIVE ("PAS-RFT2D-CSFT2D!", Prim_pas_rft2d_csft2d, 5,5, 0)
{ long i, length, power, flag, ft_type, rows,rowpower;
REAL *f1, *wcos,*w3cos,*w3sin;
void pas_rft2d(), pas_csft2d();
PRIMITIVE_HEADER (5);
CHECK_ARG (2, ARRAY_P); /* Input data (real or cs) */
CHECK_ARG (3, ARRAY_P); /* CFT twiddle tables, length = 3*(rows/4) */
CHECK_ARG (4, FIXNUM_P); /* (1)=tables precomputed, else recompute */
flag = (arg_integer (1));
f1 = ARRAY_CONTENTS(ARG_REF(2));
length = ARRAY_LENGTH(ARG_REF(2));
for (power=0, i=length; i>1; power++) /* length must be power of 2 */
{ if ( (i % 2) == 1) error_bad_range_arg(2);
i=i/2; }
if ((power % 2) == 1) error_bad_range_arg(2);
rowpower = (power/2);
rows = (1<<rowpower); /* square image */
wcos = ARRAY_CONTENTS(ARG_REF(3)); /* CFT twiddle tables */
if (ARRAY_LENGTH(ARG_REF(3)) != (3*rows/4)) error_bad_range_arg(3);
w3cos = wcos + rows/4;
w3sin = w3cos + rows/4;
ft_type = (arg_nonnegative_integer(5)); /* rft2d or csft2d */
if (ft_type == 1) {
if ((arg_nonnegative_integer(4)) == 1)
pas_rft2d (1, flag, f1, rows, rowpower, wcos,w3cos,w3sin);
else pas_rft2d (0, flag, f1, rows, rowpower, wcos,w3cos,w3sin);
}
else if (ft_type == 3) {
if ((arg_nonnegative_integer(4)) == 1)
pas_csft2d (1, flag, f1, rows, rowpower, wcos,w3cos,w3sin);
else pas_csft2d (0, flag, f1, rows, rowpower, wcos,w3cos,w3sin);
/* 1 means tables are already made
0 means compute new tables */
}
else error_bad_range_arg(5);
PRIMITIVE_RETURN (UNSPECIFIC);
}
/* c RFT2D CSFT2D
The frequencies are scrabled wrt what cft2d (and the old image-fft) give.
See cs-image-magnitude and cs-image-real which unscrable automatically.
c
c Implementation notes:
c conjugate in one domain is reverse and conjugate in other
c reverse in one domain is reverse in other
c
c reverse cs-array which is identical to conjugate cs-array (same domain)
c is reverse in other domain
c
c conjugate cs-array before csft is-better-than reverse real-array afterwards
c
c
c rft2d-csft2d use 1d-cft tables to compute rft
c cft tables are simply larger than realdata tables.
*/
/* pas_rft2d
n = rows of square image, rows is power of 2
m = rowpower
Scaling (1/n) is done all-at-once at the end.
Time-Reversing is done intermediately, it is more efficient.
*/
void pas_rft2d(tables_ok,flag, x, n,m, wcos,w3cos,w3sin)
REAL *x, *wcos,*w3cos,*w3sin;
long n,m, flag, tables_ok;
{ REAL scale, *xrow,*yrow;
long i,j, rows,cols, total_length, n2;
void pas_cft_make_twiddle_tables_once();
void C_Array_Time_Reverse();
void pas_rft_forward_loop(), pas_cft_forward_loop();
if (tables_ok != 1) /* 1 means = tables already made */
pas_cft_make_twiddle_tables_once(n,m, wcos,w3cos,w3sin);
rows = n;
cols = rows; /* square image */
n2 = n/2;
total_length = rows*rows;
xrow = x; /* First ROW-WISE */
if (flag == 1) /* forward transform */
for (i=0; i<rows; i++)
{ pas_rft_forward_loop(xrow, n,m, wcos,w3cos,w3sin);
xrow = xrow + cols; }
else /* backward transform */
for (i=0; i<rows; i++)
{ pas_rft_forward_loop(xrow, n,m, wcos,w3cos,w3sin);
for (j=n2+1; j<n; j++) xrow[j] = (-xrow[j]); /* time-reverse cs-array */
xrow = xrow + cols; }
Image_Fast_Transpose(x, rows); /* COLUMN-WISE */
/* TREAT specially rows 0 and n2,
they are real and go into cs-arrays */
if (flag == 1) /* forward transform */
{ xrow = x + 0 ;
pas_rft_forward_loop(xrow, n,m, wcos,w3cos,w3sin);
xrow = x + n2*cols;
pas_rft_forward_loop(xrow, n,m, wcos,w3cos,w3sin); }
else /* backward transform */
{ xrow = x + 0 ;
pas_rft_forward_loop(xrow, n,m, wcos,w3cos,w3sin);
for (j=n2+1; j<n; j++) xrow[j] = (-xrow[j]); /* time-reverse cs-array */
xrow = x + n2*cols;
pas_rft_forward_loop(xrow, n,m, wcos,w3cos,w3sin);
for (j=n2+1; j<n; j++) xrow[j] = (-xrow[j]); /* time-reverse cs-array */
}
/* TREAT the rest of the rows with CFT
*/
if (flag != 1) /* backward : conjugate before */
for (i=(n2+1)*cols; i<total_length; i++) x[i] = (-x[i]);
xrow = x + cols; /* real part */
yrow = x + (rows-1)*cols; /* imag part */
for (i=1; i<n2; i++) /* forward or backward transform */
{ pas_cft_forward_loop(xrow,yrow, n,m, wcos,w3cos,w3sin);
xrow = xrow + cols;
yrow = yrow - cols; }
/* DO NOT TRANSPOSE BACK, leave real-imag in horizontal rows, save.
*/
if (flag == 1) /* forward : scale */
{ scale = (REAL) (1.0 / ((double) total_length));
for (i=0; i<total_length; i++)
x[i] = x[i]*scale; }
else /* backward : conjugate after */
for (i=(n2+1)*cols; i<total_length; i++)
x[i] = (-x[i]);
}
/* pas_csft2d
n = rows of square image, rows is power of 2
m = rowpower
Scaling (1/n) is done all-at-once at the end.
Time-Reversing is done intermediately, it is more efficient.
*/
void pas_csft2d(tables_ok,flag, x, n,m, wcos,w3cos,w3sin)
REAL *x, *wcos,*w3cos,*w3sin;
long n,m, flag, tables_ok;
{ REAL scale, *xrow,*yrow;
long i,j, rows,cols, total_length, n2;
void pas_cft_make_twiddle_tables_once();
void C_Array_Time_Reverse();
void pas_csft_backward_loop(), pas_cft_forward_loop();
if (tables_ok != 1) /* 1 means = tables already made */
pas_cft_make_twiddle_tables_once(n,m, wcos,w3cos,w3sin);
rows = n;
cols = rows; /* square image */
n2 = n/2;
total_length = rows*rows;
/* First ROW-WISE */
/* TREAT SPECIALLY ROWS 0 and n2, they are cs-arrays and they go into real */
if (flag == 1) /* forward transform */
{ xrow = x + 0 ;
for (j=n2+1; j<n; j++) xrow[j]=(-xrow[j]); /* conjugate before */
pas_csft_backward_loop(xrow, n,m, wcos,w3cos,w3sin);
xrow = x + n2*cols;
for (j=n2+1; j<n; j++) xrow[j]=(-xrow[j]); /* conjugate before */
pas_csft_backward_loop(xrow, n,m, wcos,w3cos,w3sin); }
else /* backward transform */
{ xrow = x + 0 ;
pas_csft_backward_loop(xrow, n,m, wcos,w3cos,w3sin);
xrow = x + n2*cols;
pas_csft_backward_loop(xrow, n,m, wcos,w3cos,w3sin); }
/* TREAT the rest of the rows with CFT
*/
if (flag != 1) /* backward : conjugate before */
for (i=(n2+1)*cols; i<total_length; i++) x[i] = (-x[i]);
xrow = x + cols; /* real part */
yrow = x + (rows-1)*cols; /* imag part */
for (i=1; i<n2; i++) /* forward or backward transform */
{ pas_cft_forward_loop(xrow,yrow, n,m, wcos,w3cos,w3sin);
xrow = xrow + cols;
yrow = yrow - cols; }
if (flag != 1) /* backward : conjugate after */
for (i=(n2+1)*cols; i<total_length; i++) x[i] = (-x[i]);
Image_Fast_Transpose(x, rows);
/* Second COLUMN-WISE
Everything should be cs-arrays now */
xrow = x;
if (flag == 1) /* forward transform */
for (i=0; i<rows; i++)
{ for (j=n2+1; j<n; j++) xrow[j]=(-xrow[j]); /* conjugate before */
pas_csft_backward_loop(xrow, n,m, wcos,w3cos,w3sin);
xrow = xrow + cols; }
else /* backward transform */
for (i=0; i<rows; i++)
{ pas_csft_backward_loop(xrow, n,m, wcos,w3cos,w3sin);
xrow = xrow + cols; }
if (flag == 1) /* forward : scale */
{ scale = (REAL) (1.0 / ((double) total_length));
for (i=0; i<total_length; i++)
x[i] = x[i] * scale; }
}
/* STUFF BEFORE 4-15-1989
*/
void Make_FFT_Tables(w1, w2, n, flag)
REAL *w1, *w2; long n, flag; /* n = length of data */
{ long m, n2=n/2; /* n2 = length of w1,w2 */
double tm;
if (flag==1) /* FORWARD FFT */
for (m=0; m<n2; m++) {
tm = TWOPI * ((double) m) / ((double) n);
w1[m] = (REAL) cos(tm);
w2[m] = (REAL) - sin(tm); }
else
for (m=0; m<n2; m++) {
tm = TWOPI * ((double) m) / ((double) n);
w1[m] = (REAL) cos(tm);
w2[m] = (REAL) sin(tm); }
}
#define mult(pf1, pf2, pg1, pg2, w1, w2) \
{ long x, y, p2, p3, p4, p5, p6, p7; \
REAL tmp1, tmp2; \
a = a / 2; \
p2 = - a; \
p3 = 0; \
for ( x = 1; x <= n2; x = x + a ) { \
p2 = p2 + a; \
for( y = 1; y <= a; ++y ) { \
++p3; \
p4 = p2 + 1; \
p5 = p2 + p3; \
p5 = ((p5-1) % n) + 1; \
p6 = p5 + a; \
tmp1 = w1[p4-1] * pf1[p6-1] \
- w2[p4-1] * pf2[p6-1]; \
tmp2 = w1[p4-1] * pf2[p6-1] \
+ w2[p4-1] * pf1[p6-1]; \
pg1[p3-1] = pf1[p5-1] + tmp1; \
pg2[p3-1] = pf2[p5-1] + tmp2; \
p7 = p3 + n2; \
pg1[p7-1] = pf1[p5-1] - tmp1; \
pg2[p7-1] = pf2[p5-1] - tmp2; \
} \
} \
}
/* n = length of input data f1,f2;
power = log2(n),
g1,g2 are intermediate arrays of length n,
w1,w2 point to FFT tables (twiddle factors),
flag 1 for forward FFT, else inverse.
*/
/* The arrays w1,w2 are half the size of f1,f2,g1,g2.
f1,f2 contain the real and imaginary parts of the signal.
The answer is left in f1, f2.
*/
/* C_Array_FFT
complex data, radix=2, not-in-place.
(adapted from an fft program I got from Yekta)
*/
void C_Array_FFT(flag, power, n, f1, f2, g1,g2,w1,w2)
long flag, power, n; REAL f1[], f2[], g1[], g2[], w1[], w2[];
{ long n2=n>>1, a;
long i, l, m;
REAL tm;
a = n; /* initially equal to length of data */
for (m=0; m<n; m++) { g1[m] = f1[m]; g2[m] = f2[m]; }
Make_FFT_Tables(w1,w2,n, flag);
if ((power % 2) == 1) l = 2; else l = 1; /* even,odd power */
for ( i = l; i <= power ; i = i + 2 ) {
mult(g1,g2,f1,f2,w1,w2);
mult(f1,f2,g1,g2,w1,w2); }
if (flag==1) { /* FORWARD FFT */
tm = 1. / ((REAL) n); /* normalizing factor */
if (l==1) /* even power */
for (m=0; m<n; m++) { f1[m] = tm * g1[m]; f2[m] = tm * g2[m]; }
else { /* odd power ==> do one more mult */
mult(g1,g2,f1,f2,w1,w2); /* f1 and f2 contain the result now */
for (m=0; m<n; m++) { f1[m] = tm * f1[m]; f2[m] = tm * f2[m]; }}
}
else { /* BACKWARD FFT */
if (l==1) /* even power */
for (m=0; m<n; m++) { f1[m] = g1[m]; f2[m] = g2[m]; }
else /* odd power ==> do one more mult */
mult(g1,g2,f1,f2,w1,w2); /* f1 and f2 contain the result now */
}
}
void C_Array_FFT_With_Given_Tables(flag, power, n, f1, f2, g1,g2,w1,w2)
long flag, power, n; REAL f1[], f2[], g1[], g2[], w1[], w2[];
{ long n2=n>>1, a;
long i, l, m;
REAL tm;
a = n; /* initially equal to length */
for (m=0; m<n; m++) { g1[m] = f1[m]; g2[m] = f2[m]; }
if ((power % 2) == 1) l = 2; else l = 1; /* even,odd power */
for ( i = l; i <= power ; i = i + 2 ) {
mult(g1,g2,f1,f2,w1,w2);
mult(f1,f2,g1,g2,w1,w2); }
if (flag==1) { /* FORWARD FFT */
tm = 1. / ((REAL) n); /* normalizing factor */
if (l==1) /* even power */
for (m=0; m<n; m++) { f1[m] = tm * g1[m]; f2[m] = tm * g2[m]; }
else { /* odd power ==> do one more mult */
mult(g1,g2,f1,f2,w1,w2); /* f1 and f2 contain the result now */
for (m=0; m<n; m++) { f1[m] = tm * f1[m]; f2[m] = tm * f2[m]; }}
}
else { /* BACKWARD FFT */
if (l==1) /* even power */
for (m=0; m<n; m++) { f1[m] = g1[m]; f2[m] = g2[m]; }
else /* odd power ==> do one more mult */
mult(g1,g2,f1,f2,w1,w2); /* f1 and f2 contain the result now */
}
}
/* CHIRP-Z-TRANSFORM (complex data)
*/
/* C_Array_CZT Generalization of DFT
;;
Frequency is scaled as an L/2-point DFT of the input data (zero padded to L/2).
;;
phi = starting point (on unit circle) -- Range 0,1 (covers 0,2pi like DFT angle)
rho = resolution (angular frequency spacing) -- Range 0,1 (maps 0,2pi like DFT angle)
N = input data length
M = output data length
log2_L = smallest_power_of_2_ge(N+M-1) ----
f1,f2 contain the input data (complex).
f1,f2,fo1,fo2,g1,g2 must be of length L
fft_w1,fft_w2 must be of length L/2
czt_w1,czt_w2 must be of length max(M,N) ----
;;
RESULT is left on f1,f2 (M complex numbers).
*/
C_Array_CZT(phi,rho, N,M,log2_L, f1,f2,fo1,fo2, g1,g2, fft_w1,fft_w2,czt_w1,czt_w2)
double phi, rho;
REAL *f1,*f2,*fo1,*fo2, *g1,*g2, *fft_w1,*fft_w2,*czt_w1,*czt_w2;
long N,M,log2_L;
{ long i, maxMN, L, L2;
void Make_CZT_Tables(), CZT_Pre_Multiply(), Make_Chirp_Filter();
void Make_FFT_Tables(), C_Array_FFT_With_Given_Tables(), C_Array_Complex_Multiply_Into_First_One();
maxMN = max(M,N);
L = 1<<log2_L;
L2 = L/2;
CZT_Pre_Multiply(phi,rho, f1,f2, N,L);
Make_FFT_Tables(fft_w1,fft_w2, L, 1); /* PREPARE TABLES FOR FORWARD FFT */
C_Array_FFT_With_Given_Tables(1, log2_L, L, f1,f2, g1,g2, fft_w1,fft_w2);
Make_CZT_Tables(czt_w1,czt_w2, rho, maxMN);
Make_Chirp_Filter(fo1,fo2, N,M,L, czt_w1,czt_w2);
C_Array_FFT_With_Given_Tables(1, log2_L, L, fo1,fo2, g1,g2, fft_w1,fft_w2);
C_Array_Complex_Multiply_Into_First_One(f1,f2, fo1,fo2, L);
for (i=0;i<L2;i++) fft_w2[i] = (-fft_w2[i]); /* PREPARE TABLES FOR INVERSE FFT */
C_Array_FFT_With_Given_Tables(-1, log2_L, L, f1,f2, g1,g2, fft_w1,fft_w2);
C_Array_Complex_Multiply_Into_First_One(f1,f2, czt_w1,czt_w2, M);
}
void CZT_Pre_Multiply(phi,rho, f1,f2, N,L) /* phi = starting point */
double phi,rho; REAL *f1,*f2; long N,L; /* this proc is two complex multiplication */
{ long i;
double tmp, A1, A2;
rho = rho*.5; /* To make 1/2 in exponent "(n^2)/2" */
for (i=0;i<N;i++)
{ tmp = ((double) i);
tmp = TWOPI * ((phi + (rho*tmp)) * tmp);
A1 = cos(tmp);
A2 = sin(tmp);
tmp = A1*f1[i] - A2*f2[i];
f2[i] = (REAL) (A1*f2[i] + A2*f1[i]);
f1[i] = (REAL) tmp;
}
for (i=N;i<L;i++) { f1[i] = 0.0; /* zero pad */
f2[i] = 0.0; }
}
void Make_Chirp_Filter(fo1,fo2, N,M,L, czt_w1,czt_w2)
REAL *fo1,*fo2, *czt_w1,*czt_w2; long N,M,L;
{ long i, L_minus_N_plus_1 = L-N+1;
for (i=0;i<M;i++) { fo1[i] = czt_w1[i];
fo2[i] = - czt_w2[i]; }
for (i=M;i<L_minus_N_plus_1;i++) { fo1[i] = 0.0; /* arbitrary region, circular convolution */
fo2[i] = 0.0; }
for (i=L_minus_N_plus_1;i<L;i++) { fo1[i] = czt_w1[(L-i)];
fo2[i] = - czt_w2[(L-i)]; }
}
void Make_CZT_Tables(czt_w1,czt_w2, rho, maxMN) /* rho = resolution */
double rho; REAL *czt_w1,*czt_w2; long maxMN;
{ long i;
double tmp;
rho = rho*.5; /* the 1/2 in the "(n^2)/2" exponent */
for (i=0;i<maxMN;i++)
{ tmp = ((double) i);
tmp = TWOPI * (tmp * (rho*tmp));
czt_w1[i] = (REAL) cos(tmp);
czt_w2[i] = (REAL) sin(tmp); }
}
long smallest_power_of_2_ge(n)
long n;
{ long i,power;
if (n<0) { printf("\n ABORT program! smallest_pwr_of_2_ge negative argument--- %d\n", n); fflush(stdout); }
power=0; i=1;
while (i<n)
{ power++; i=i*2; }
return(power);
}
/* stuff not currently used
void CZT_Post_Multiply(f1,f2,czt_w1,czt_w2,M)
REAL *f1,*f2,*czt_w1,*czt_w2; long M;
{ long i;
REAL tmp;
for (i=0;i<M;i++)
{ tmp = f1[i]*czt_w1[i] - f2[i]*czt_w2[i];
f2[i] = 2.0 * (f1[i]*czt_w2[i] + f2[i]*czt_w1[i]);
f1[i] = 2.0 * tmp;
}}
#define take_modulo_one(x, answer) \
{ long ignore_integral_part; \
double modf(); \
answer = (double) modf( ((double) x), &ignore_integral_part); }
^ this only works when answer is double
*/
/* 2D DFT ---------------- row-column decomposition
(3D not working yet)
*/
C_Array_2D_FFT_In_Scheme_Heap(flag, nrows, ncols, Real_Array, Imag_Array)
long flag, nrows, ncols; REAL *Real_Array, *Imag_Array;
{ long i, j;
REAL *Temp_Array;
REAL *f1,*f2,*g1,*g2,*w1,*w2, *Work_Here;
long nrows_power, ncols_power, Length = nrows*ncols;
if (nrows==ncols) { /* SQUARE IMAGE, OPTIMIZE... */
Square_Image_2D_FFT_In_Scheme_Heap(flag, nrows, Real_Array, Imag_Array);
}
else { /* NOT A SQUARE IMAGE, CANNOT DO FAST_TRANSPOSE */
/* FIRST (NCOLS-1)POINT FFTS FOR EACH ROW, THEN (NROWS-1)POINT FFTS FOR EACH COLUMN */
for (ncols_power=0, i=ncols; i>1; ncols_power++) { /* FIND/CHECK POWERS OF ROWS,COLS */
if ( (i % 2) == 1) error_bad_range_arg (2);
i=i/2; }
for (nrows_power=0, i=nrows; i>1; nrows_power++) {
if ( (i % 2) == 1) error_bad_range_arg (1);
i=i/2; }
#if (REAL_IS_DEFINED_DOUBLE != 0)
ALIGN_FLOAT (Free);
Free += 1;
#endif
Primitive_GC_If_Needed(Length*REAL_SIZE + ((max(nrows,ncols))*3*REAL_SIZE));
Work_Here = (REAL *) Free;
g1 = Work_Here;
g2 = Work_Here + ncols;
w1 = Work_Here + (ncols<<1);
w2 = Work_Here + (ncols<<1) + (ncols>>1);
Make_FFT_Tables(w1,w2,ncols, flag);
for (i=0;i<nrows;i++) { /* ROW-WISE */
f1 = Real_Array + (i*ncols);
f2 = Imag_Array + (i*ncols);
C_Array_FFT_With_Given_Tables(flag, ncols_power, ncols, f1,f2,g1,g2,w1,w2);
}
Temp_Array = Work_Here;
Work_Here = Temp_Array + Length;
Image_Transpose(Real_Array, Temp_Array, nrows, ncols); /* TRANSPOSE: (1) order of frequencies. (2) read columns.*/
Image_Transpose(Imag_Array, Real_Array, nrows, ncols);
g1 = Work_Here;
g2 = Work_Here + nrows;
w1 = Work_Here + (nrows<<1);
w2 = Work_Here + (nrows<<1) + (nrows>>1);
Make_FFT_Tables(w1,w2,nrows,flag);
for (i=0;i<ncols;i++) { /* COLUMN-WISE */
f1 = Temp_Array + (i*nrows); /* THIS IS REAL DATA */
f2 = Real_Array + (i*nrows); /* THIS IS IMAG DATA */
C_Array_FFT_With_Given_Tables(flag, nrows_power, nrows, f1,f2,g1,g2,w1,w2);
}
Image_Transpose(Real_Array, Imag_Array, ncols, nrows); /* DO FIRST THIS !!!, do not screw up Real_Data !!! */
Image_Transpose(Temp_Array, Real_Array, ncols, nrows); /* TRANSPOSE BACK: order of frequencies. */
}
}
Square_Image_2D_FFT_In_Scheme_Heap(flag, nrows, Real_Array, Imag_Array)
long flag,nrows; REAL *Real_Array, *Imag_Array;
{ REAL *f1,*f2,*g1,*g2,*w1,*w2, *Work_Here;
long nrows_power;
long i;
for (nrows_power=0, i=nrows; i>1; nrows_power++) { /* FIND/CHECK POWERS OF ROWS */
if ( (i % 2) == 1) error_bad_range_arg (2);
i=i/2; }
#if (REAL_IS_DEFINED_DOUBLE != 0)
ALIGN_FLOAT (Free);
Free += 1;
#endif
Primitive_GC_If_Needed(nrows*3*REAL_SIZE);
Work_Here = (REAL *) Free;
g1 = Work_Here;
g2 = Work_Here + nrows;
w1 = Work_Here + (nrows<<1);
w2 = Work_Here + (nrows<<1) + (nrows>>1);
Make_FFT_Tables(w1, w2, nrows, flag); /* MAKE TABLES */
for (i=0;i<nrows;i++) { /* ROW-WISE */
f1 = Real_Array + (i*nrows);
f2 = Imag_Array + (i*nrows);
C_Array_FFT_With_Given_Tables(flag, nrows_power, nrows, f1,f2,g1,g2,w1,w2);
}
Image_Fast_Transpose(Real_Array, nrows); /* MUST TRANSPOSE (1) order of frequencies. (2) read columns. */
Image_Fast_Transpose(Imag_Array, nrows);
for (i=0;i<nrows;i++) { /* COLUMN-WISE */
f1 = Real_Array + (i*nrows);
f2 = Imag_Array + (i*nrows);
C_Array_FFT_With_Given_Tables(flag, nrows_power, nrows, f1,f2,g1,g2,w1,w2); /* ncols=nrows... Twiddles... */
}
Image_Fast_Transpose(Real_Array, nrows); /* TRANSPOSE BACK: order of frequencies. */
Image_Fast_Transpose(Imag_Array, nrows);
}
C_Array_3D_FFT_In_Scheme_Heap(flag, ndeps, nrows, ncols, Real_Array, Imag_Array)
long flag, ndeps, nrows, ncols; REAL *Real_Array, *Imag_Array;
{ long l, m, n;
REAL *Temp_Array;
REAL *f1,*f2,*g1,*g2,*w1,*w2, *Work_Here;
long ndeps_power, nrows_power, ncols_power;
if ((ndeps==nrows) && (nrows==ncols)) { /* CUBIC IMAGE, OPTIMIZE... */
Cube_Space_3D_FFT_In_Scheme_Heap(flag, ndeps, Real_Array, Imag_Array);
}
else {
for (ndeps_power=0, l=ndeps; l>1; ndeps_power++) { /* FIND/CHECK POWERS OF DEPS,ROWS,COLS */
if ( (l % 2) == 1) error_bad_range_arg (2);
l=l/2; }
for (nrows_power=0, m=nrows; m>1; nrows_power++) {
if ( (m % 2) == 1) error_bad_range_arg (2);
m=m/2; }
for (ncols_power=0, n=ncols; n>1; ncols_power++) {
if ( (n % 2) == 1) error_bad_range_arg (2);
n=n/2; }
printf("3D FFT implemented only for cubic-spaces.\n");
printf("aborted\n.");
}
}
Cube_Space_3D_FFT_In_Scheme_Heap(flag, ndeps, Real_Array, Imag_Array)
long flag, ndeps; REAL *Real_Array, *Imag_Array;
{ fast long l, m, n;
fast long ndeps_power, Surface_Length;
fast REAL *From_Real, *From_Imag;
fast REAL *f1,*f2,*g1,*g2,*w1,*w2, *Work_Here;
for (ndeps_power=0, l=ndeps; l>1; ndeps_power++) { /* FIND/CHECK POWER OF NDEPS */
if ( (l % 2) == 1) error_bad_range_arg (2);
l=l/2; }
#if (REAL_IS_DEFINED_DOUBLE != 0)
ALIGN_FLOAT (Free);
Free += 1;
#endif
Primitive_GC_If_Needed(ndeps*3*REAL_SIZE);
Work_Here = (REAL *) Free;
g1 = Work_Here;
g2 = Work_Here + ndeps;
w1 = Work_Here + (ndeps<<1);
w2 = Work_Here + (ndeps<<1) + (ndeps>>1);
Make_FFT_Tables(w1, w2, ndeps, flag); /* MAKE TABLES */
Surface_Length=ndeps*ndeps;
From_Real = Real_Array; From_Imag = Imag_Array;
for (l=0; l<ndeps; l++,From_Real+=Surface_Length,From_Imag+=Surface_Length) { /* DEPTH-WISE */
f1 = From_Real; f2 = From_Imag;
for (m=0; m<ndeps; m++,f1+=ndeps,f2+=ndeps) { /* ROW-WISE */
C_Array_FFT_With_Given_Tables(flag, ndeps_power, ndeps, f1,f2,g1,g2,w1,w2); }
Image_Fast_Transpose(From_Real, ndeps); /* MUST TRANSPOSE (1) order of frequencies. (2) read columns. */
Image_Fast_Transpose(From_Imag, ndeps);
/* ndeps=nrows=ncols, same Twiddle Tables */
f1 = From_Real; f2 = From_Imag;
for (n=0; n<ndeps; n++,f1+=ndeps,f2+=ndeps) { /* COLUMN-WISE */
C_Array_FFT_With_Given_Tables(flag, ndeps_power, ndeps, f1,f2,g1,g2,w1,w2); }
Image_Fast_Transpose(From_Real, ndeps); /* TRANSPOSE BACK: order of frequencies. */
Image_Fast_Transpose(From_Imag, ndeps);
}
}
/*----------------------- scheme primitives ------------------------- */
/* Real and Imag arrays must be different.
Arg1=1 --> forward FFT, otherwise backward.
*/
DEFINE_PRIMITIVE ("ARRAY-FFT!", Prim_array_fft, 3, 3, 0)
{ long length, power, flag, i;
SCHEME_OBJECT answer;
REAL *f1,*f2,*g1,*g2,*w1,*w2;
REAL *Work_Here;
PRIMITIVE_HEADER (4);
flag = arg_integer(1); /* forward or backward */
CHECK_ARG (2, ARRAY_P); /* input real */
CHECK_ARG (3, ARRAY_P); /* input imag */
length = ARRAY_LENGTH(ARG_REF(2));
if (length != (ARRAY_LENGTH(ARG_REF(3)))) error_bad_range_arg(2);
for (power=0, i=length; i>1; power++) {
if ( (i % 2) == 1) error_bad_range_arg(2);
i=i/2; }
f1 = ARRAY_CONTENTS(ARG_REF(2));
f2 = ARRAY_CONTENTS(ARG_REF(3));
if (f1==f2) error_wrong_type_arg(2);
#if (REAL_IS_DEFINED_DOUBLE != 0)
ALIGN_FLOAT (Free);
Free += 1;
#endif
Primitive_GC_If_Needed(length*3*REAL_SIZE);
Work_Here = (REAL *) Free;
g1 = Work_Here;
g2 = Work_Here + length;
w1 = Work_Here + (length<<1);
w2 = Work_Here + (length<<1) + (length>>1);
C_Array_FFT(flag, power, length, f1,f2,g1,g2,w1,w2);
PRIMITIVE_RETURN (UNSPECIFIC);
}
DEFINE_PRIMITIVE ("ARRAY-CZT!", Prim_array_czt, 6,6, 0)
{ double phi,rho;
long N,M,L, i;
long log2_L, maxMN;
long smallest_power_of_2_ge();
int errcode;
REAL *a,*b,*c,*d;
REAL *f1,*f2,*fo1,*fo2, *g1,*g2, *fft_w1,*fft_w2,*czt_w1,*czt_w2, *Work_Here;
PRIMITIVE_HEADER (6);
phi = (arg_real_number (1)); /* starting point [0,1]*/
rho = (arg_real_number (2)); /* resolution [0,1] */
CHECK_ARG (3, ARRAY_P); /* input real */
CHECK_ARG (4, ARRAY_P); /* input imag */
CHECK_ARG (5, ARRAY_P); /* output real */
CHECK_ARG (6, ARRAY_P); /* output imag */
a = ARRAY_CONTENTS(ARG_REF(3));
b = ARRAY_CONTENTS(ARG_REF(4));
c = ARRAY_CONTENTS(ARG_REF(5));
d = ARRAY_CONTENTS(ARG_REF(6));
N = ARRAY_LENGTH(ARG_REF(3)); /* N = input length */
M = ARRAY_LENGTH(ARG_REF(5)); /* M = output length */
if (N!=(ARRAY_LENGTH(ARG_REF(4)))) error_bad_range_arg(3);
if (M!=(ARRAY_LENGTH(ARG_REF(6)))) error_bad_range_arg(5);
if ((M+N-1) < 4) error_bad_range_arg(5);
log2_L = smallest_power_of_2_ge(M+N-1);
L = 1<<log2_L; /* length of intermediate computation arrays */
maxMN = (((M)<(N)) ? (N) : (M)); /* length of czt tables = maximum(M,N) */
#if (REAL_IS_DEFINED_DOUBLE != 0)
ALIGN_FLOAT (Free);
Free += 1;
#endif
Primitive_GC_If_Needed( ((7*L) + (2*maxMN)) * REAL_SIZE);
g1 = (REAL *) Free;
g2 = g1 + L;
fo1 = g2 + L;
fo2 = fo1 + L;
fft_w1 = fo2 + L;
fft_w2 = fft_w1 + (L/2);
czt_w1 = fft_w2 + (L/2);
czt_w2 = czt_w1 + maxMN;
f1 = czt_w2 + maxMN; /* CZT stores its results here */
f2 = f1 + L;
for (i=0; i<N; i++) { f1[i] = a[i]; /* input data */
f2[i] = b[i]; }
C_Array_CZT(phi,rho, N,M,log2_L, f1,f2,fo1,fo2, g1,g2, fft_w1,fft_w2,czt_w1,czt_w2);
for (i=0; i<M; i++) { c[i] = f1[i]; /* results */
d[i] = f2[i]; }
PRIMITIVE_RETURN (UNSPECIFIC);
}
DEFINE_PRIMITIVE ("ARRAY-2D-FFT!", Prim_array_2d_fft, 5, 5, 0)
{
PRIMITIVE_HEADER (5);
{
fast long nrows = (arg_integer_in_range (2, 1, 513));
fast long ncols = (arg_integer_in_range (3, 1, 513));
fast SCHEME_OBJECT real_image = (ARG_REF (4));
fast SCHEME_OBJECT imag_image = (ARG_REF (5));
CHECK_ARG (4, ARRAY_P);
CHECK_ARG (5, ARRAY_P);
if (real_image == imag_image)
error_wrong_type_arg (5);
Set_Time_Zone (Zone_Math);
{
long length = (ARRAY_LENGTH (real_image));
if ((length != (ARRAY_LENGTH (imag_image))) ||
(length != (nrows * ncols)))
error_bad_range_arg (5);
}
C_Array_2D_FFT_In_Scheme_Heap
((arg_integer (1)), /* flag 1=forward else backward */
nrows,
ncols,
(ARRAY_CONTENTS (real_image)),
(ARRAY_CONTENTS (imag_image)));
PRIMITIVE_RETURN (cons (real_image, (cons (imag_image, EMPTY_LIST))));
}
}
DEFINE_PRIMITIVE ("ARRAY-3D-FFT!", Prim_array_3d_fft, 6, 6, 0)
{
PRIMITIVE_HEADER (6);
{
fast long ndeps = (arg_integer_in_range (2, 1, 513));
fast long nrows = (arg_integer_in_range (3, 1, 513));
fast long ncols = (arg_integer_in_range (4, 1, 513));
fast SCHEME_OBJECT real_image = (ARG_REF (5));
fast SCHEME_OBJECT imag_image = (ARG_REF (6));
CHECK_ARG (5, ARRAY_P);
CHECK_ARG (6, ARRAY_P);
if (real_image == imag_image)
error_wrong_type_arg (6);
{
long length = (ARRAY_LENGTH (real_image));
if ((length != (ARRAY_LENGTH (imag_image))) ||
(length != (ndeps * nrows * ncols)))
error_bad_range_arg (6);
}
C_Array_3D_FFT_In_Scheme_Heap
((arg_integer (1)),
ndeps,
nrows,
ncols,
(ARRAY_CONTENTS (real_image)),
(ARRAY_CONTENTS (imag_image)));
PRIMITIVE_RETURN (cons (real_image, (cons (imag_image, EMPTY_LIST))));
}
}